#include "language.h"
#include "syntax_compound.h"
#include "parse_info.h"
#include <iostream>
#include <map>

using namespace std;

namespace syntax_analysis
{
	syntax_type syntax_compound::get_type()
	{
		return SYNTAX_COMPOUND;
	}

	string syntax_compound::to_string()
	{
		list<syntax*>::iterator i;
		string syn;
		
		if( m_compound_type == COMPOUND_OR )
			syn += "\'";
		else if( m_child.size() > 1 && m_attr != NULL )
			syn += "{=";

		for( i = m_child.begin(); i != m_child.end(); ++i )
		{
			if( (*i)->get_type() == SYNTAX_COMPOUND &&
				((syntax_compound*)(*i))->get_child().size() > 1 &&
				((syntax_compound*)(*i))->get_compound_type() != m_compound_type &&
				m_child.size() > 1 )
				syn += "{=";
			syn += (*i)->to_string();
			if( (*i)->get_type() == SYNTAX_COMPOUND &&
				((syntax_compound*)(*i))->get_child().size() > 1 &&
				((syntax_compound*)(*i))->get_compound_type() != m_compound_type &&
				m_child.size() > 1 )
				syn += "}";
		}

		if( m_compound_type == COMPOUND_OR )
			syn += "\'";
		else if( m_child.size() > 1 && m_attr != NULL )
			syn += "}";

		if( m_attr != NULL )
			syn += m_attr->to_string();

		return syn;
	}
	
	bool syntax_compound::reparse( parse_info *info )
	{
		parse_info *child_info = NULL;
		parse_para *child_para;
		parse_para *para;
		bool result = false;

		para = info->get_parse_para();
		if( para == NULL )
			return false;
		
		inc_called();
		para->push_call( this );
		
		if( m_compound_type == COMPOUND_AND && 
			info->get_child().size() != m_child.size() &&
			info->get_child().size() != 0 ||
			m_compound_type == COMPOUND_OR && 
			info->get_child().size() > 1 )
			debug_info( info );

		if( para->get_acceptted_count() > 0 && para->get_acceptted_syn( para->get_acceptted_count()-1 ) != this )
			debug_info( info );

		if( info->get_child().size() > 0 )
		{
			syntax *syn;
			child_info = info->get_child( info->get_child().size() - 1 );
			child_para = child_info->get_parse_para();
			syn = child_para->get_acceptted_syn( child_para->get_acceptted_count()-1 );
			if( !is_my_child( syn ) )
				debug_info( info );
			result = syn->reparse( child_info );
			if( result )
			{
				if( m_attr == NULL || (m_attr->get_match_type()&DONT_TAKE_STRING) == 0 )
				{
					para->remove_last_acceptted();
					result = para->add_acceptted( this, child_para->get_last_start_code() - para->get_code() );

					if( m_compound_type == COMPOUND_AND && info->get_child().size() != m_child.size() ||
						m_compound_type == COMPOUND_OR && info->get_child().size() != 1 )
						debug_info( info );
				}
			}
		}
		
		if( !result && can_accept_more( para ) )
		{
			result = do_parse( info );
		}

		if( !result )
		{
			para->get_language()->set_error( para->get_callstack(), para->get_last_start_code() );
			para->pop_call();
		}
		else
		{
			para->pop_call();
		}

		para->set_parsed();
		
		return result;
	}

	bool syntax_compound::parse( parse_info *info )
	{
		parse_para *para;
		bool result = false;

		para = info->get_parse_para();
		if( para == NULL )
			return false;

		inc_called();
		para->push_call( this );

		if( should_accept_more( para ) )
		{
			do 
			{
				result = do_parse( info );

			} while( result && should_accept_more( para ) );
		}
		else if( !para->parsed() )
		{
			result = true;
		}

		if( result && 
			( m_attr == NULL ||
			  (m_attr->get_match_type()&MAX_MATCH) == MAX_MATCH ) )
		{
			while( can_accept_more( para ) && do_parse( info ) )
			{
				if( m_attr != NULL && (m_attr->get_match_type()&DONT_TAKE_STRING) == DONT_TAKE_STRING )
					break;
			}
		}
		
		if( !result )
		{
			para->get_language()->set_error( para->get_callstack(), para->get_last_start_code() );
			para->pop_call();
		}
		else
		{
			para->pop_call();
		}

		para->set_parsed();

		return result;
	}
	
	bool syntax_compound::do_parse( parse_info *info )
	{
		bool result = false;
		parse_info *child_info;
		parse_para *child_para;
		parse_para *para;
		list<syntax*>::iterator i;
		int pos = 0;
		char *last_code;
		bool is_reparse = false;
		
		info->clear_child();

		para = info->get_parse_para();
		last_code = para->get_last_start_code();
		
		for( i = m_child.begin(); i != m_child.end();  )
		{
			child_info = info->get_child( pos );
			if( child_info == NULL )
				child_info = info->create_child( last_code, para->get_language() );

			if( is_reparse )
			{
				child_para = child_info->get_parse_para();
				if( child_para->get_acceptted_count() > 0 &&
					child_para->get_acceptted_syn( child_para->get_acceptted_count()-1 ) != *i )
					(*i)->debug_info( child_info );
				result = (*i)->reparse( child_info );
			}
			else
			{
				result = (*i)->parse( child_info );
			}
			child_para = child_info->get_parse_para();
			last_code = child_para->get_last_start_code();

			if( m_compound_type == COMPOUND_OR )
			{
				if( result )
					break;
				info->remove_child( 0 );
				++i, ++pos;
			}
			else
			{
				if( !result )
				{
					info->remove_child( pos );
					if( i == m_child.begin() )
						break;
					else
					{
						--i, --pos;
						is_reparse = true;
					}
				}
				else
				{
					++i, ++pos;
					is_reparse = false;
				}
			}
		}
		if( result )
		{
			if( m_compound_type == COMPOUND_AND && info->get_child().size() != m_child.size() ||
				m_compound_type == COMPOUND_OR && info->get_child().size() != 1 )
				debug_info( info );

			if( m_attr == NULL || (m_attr->get_match_type()&DONT_TAKE_STRING) == 0 )
				result = para->add_acceptted( this, last_code - para->get_code() );
			else
				result = para->add_acceptted( this, para->get_acceptted( para->get_acceptted_count() - 1 ), false );
		}
		return result;
	}

	bool syntax_compound::can_accept_more( parse_para *para )
	{
		int nmax;
		int real = para->get_acceptted_count();
		if( m_attr == NULL )
			return real < 1;
		
		nmax = m_attr->get_max();
		if( nmax >= 0 )
			return real < nmax;
		return true;
	}

	bool syntax_compound::should_accept_more( parse_para *para )
	{
		int nmin;
		int real = para->get_acceptted_count();
		if( m_attr == NULL )
			return real < 1;
		
		nmin = m_attr->get_min();
		if( nmin >= 0 )
			return real < nmin;
		else if( nmin == EXP_ONE_MORE )
			return real < 1;
		return false;
	}

}